#include <linux/module.h>   
#include <linux/init.h>  
#include <linux/fs.h>   
#include <linux/device.h>   
#include <linux/slab.h>  
#include <linux/cdev.h>  
#include <linux/err.h>  
#include <asm/uaccess.h>  
#include <linux/io.h>  
#include <linux/of.h>  
#include <linux/of_gpio.h>  
#include <linux/gpio.h>  
#include <linux/platform_device.h>
#include <linux/kern_levels.h>
#include <linux/ioport.h>      
#include <linux/of_address.h>
#include <linux/of_irq.h>
#include <linux/interrupt.h>
#include <linux/device.h>
#include <linux/uaccess.h>
#include <linux/wait.h>
#include <linux/poll.h>
#include <net/sock.h>
#include <linux/netlink.h>
#include <linux/semaphore.h>
#include "netlink_u.h"
 

#define NETLINK_PAYLOAD_LEN (128)
#define NETLINK_TEST     40



typedef struct netlink_socket
{
   struct sock* n_sock;
   struct semaphore flag_lock;
   recv_data_netlink cb;

}netlink_socket_t;

static netlink_socket_t g_netlink_socket = {0};
 

//   struct nlmsghdr {
// 	__u32		nlmsg_len;	/* Length of message including header */
// 	__u16		nlmsg_type;	/* Message content */
// 	__u16		nlmsg_flags;	/* Additional flags */
// 	__u32		nlmsg_seq;	/* Sequence number */
// 	__u32		nlmsg_pid;	/* Sending process port ID */
//    };


static void netlink_input(struct sk_buff *__skb)
{
    struct sk_buff *skb = NULL;
    struct nlmsghdr *nl_msghdr = NULL;
    int user_data_len = 0;
    char* data = NULL;
    if(__skb == NULL)
    {
        printk("sk_buff is NULL\n");
        return;
    }
    //添加引用计数，防止中途被释放
    skb = skb_get(__skb);

    nl_msghdr = nlmsg_hdr(skb);
    if(skb->len < NLMSG_SPACE(0))
    {
        printk(KERN_INFO"message length error, len = %d\n", skb->len);
        goto INPUT_EXIT;
    }

    user_data_len = nl_msghdr->nlmsg_len - NLMSG_SPACE(0);

    if(user_data_len > 0)
    {
        data = NLMSG_DATA(nl_msghdr);
        printk("netlink get data_len = %d, pid = %d, data = %s type=%d\n", user_data_len, nl_msghdr->nlmsg_pid, data, nl_msghdr->nlmsg_type);
        g_netlink_socket.cb(data, user_data_len);
    }

INPUT_EXIT:
    kfree_skb(skb);
    return;
}

int netlink_create_sock(recv_data_netlink cb)
{
       struct netlink_kernel_cfg nl_cfg =
       {
           .input = netlink_input,
       };
       
      g_netlink_socket.n_sock = netlink_kernel_create(&init_net, NETLINK_TEST, &nl_cfg);
      g_netlink_socket.cb = cb;
      
      if(g_netlink_socket.n_sock == NULL)
      {
         printk("create socket error\n");
         return -1;
      }


      return 0;
}


void netlink_release_sock(void)
{
     netlink_kernel_release(g_netlink_socket.n_sock);
}
 

int netlink_send_data(char* data, int len, int pid)
{
    
    struct nlmsghdr *nl_msghdr = NULL;
    struct sk_buff  *skb_send  = NULL;
    int data_len = 0;

    if(NULL == data)
    {
        printk("data is NULL\n");
        return -1;
    }

    if(len > NETLINK_PAYLOAD_LEN)
    {
        printk("length(%d) is out of range\n", len);
        return -1;
    }

    data_len = NLMSG_SPACE(NETLINK_PAYLOAD_LEN);

    skb_send = alloc_skb(data_len, GFP_KERNEL);
    if(NULL == skb_send)
    {
        printk("alloc skb error\n");
        return -1;
    }

    nl_msghdr = nlmsg_put(skb_send, 0, 0, 0, data_len - NLMSG_SPACE(0), 0);
    memcpy(NLMSG_DATA(nl_msghdr), data, len);
    netlink_unicast(g_netlink_socket.n_sock, skb_send, pid, 0);
    return 0;

}
 

int netlink_send_data_broadcast(char* data, int len)
{
    struct nlmsghdr *nl_msghdr = NULL;
    struct sk_buff  *skb_send  = NULL;
    int data_len = 0;
    if(NULL == data)
    {
        printk("data is NULL\n");
        return -1;
    }

    if(len > NETLINK_PAYLOAD_LEN)
    {
        printk("length(%d) is out of range\n", len);
        return -1;
    }

    data_len = NLMSG_SPACE(NETLINK_PAYLOAD_LEN);

    skb_send = alloc_skb(data_len, GFP_KERNEL);
    if(NULL == skb_send)
    {
        printk("alloc skb error\n");
        return -1;
    }

    nl_msghdr = nlmsg_put(skb_send, 0, 0, 0, data_len - NLMSG_SPACE(0), 0);
    memcpy(NLMSG_DATA(nl_msghdr), data, len);
    netlink_broadcast(g_netlink_socket.n_sock, skb_send, 0, NETLINK_TEST, GFP_ATOMIC);
    return 0;
}
 
 
MODULE_LICENSE("GPL");  